/*
 * Copyright (c) 2007 Compl Yue Still, All Rights Reserved.
 */

package hbi.domain.java;

import hbi.domain.DomainController;
import hbi.domain.Gateway;
import hbi.task.TaskAgent;
import hbi.task.TaskScript;
import hbi.wire.java.guest.ByteBufferOutputStream;
import hbi.wire.java.guest.GuestEnd;

import java.io.IOException;
import java.lang.reflect.Proxy;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;

public class JavaGateway<L extends JavaDomain, R extends DomainController>
    implements Gateway<L, R>
{

    public final L localDomain;

    private final ByteBufferOutputStream buffers;

    private final GuestEnd guestEnd;

    public JavaGateway(L localDomain, Class<? extends R> remoteDomainType)
    {
        this.localDomain = localDomain;
        this.buffers = new ByteBufferOutputStream(1024);
        this.guestEnd = new GuestEnd(buffers);

        localDomain.localGatewaySetup(this, remoteDomainType);
    }

    public ByteBuffer pollOutgoingData()
    {
        return buffers.pollData();
    }

    @SuppressWarnings("unchecked")
    public <T extends TaskAgent<? super R, ? super L>> void sendTaskAgent(
        Class<T> agentClass, TaskScript<? super T> script)
    {
        try
        {
            synchronized (guestEnd)
            {
                guestEnd.startAgent(agentClass);
                if (script != null)
                {
                    TaskAgent ta = (TaskAgent) Proxy.newProxyInstance(
                        agentClass.getClassLoader(),
                        new Class<?>[] { agentClass }, new LocalAgentStub(
                            guestEnd));
                    ((TaskScript) script).run(ta);
                }
                guestEnd.finishAgent();
            }
            this.selKey.selector().wakeup();
        }
        catch (IOException e)
        {
            // TODO: throw an exception handled by the framework
            throw new RuntimeException(e);
        }
    }

    private SelectionKey selKey;

    public void setSelectionKey(SelectionKey selKey)
    {
        this.selKey = selKey;
    }

    public boolean isAlive()
    {
        return this.selKey.channel().isOpen();
    }

    public void close() throws IOException
    {
        this.selKey.channel().close();
    }

    public final L getLocalDomain()
    {
        return this.localDomain;
    }

}
